import java.io.*;
import java.lang.reflect.*;
import java.nio.file.*;
import java.util.*;
import java.util.ArrayList;
// ========== 数据类 ==========
class SaleItem {
    public final String id;
    public final String category;
    public final int count;
    public final int unitPrice;

    public SaleItem(String id, String category, int count, int unitPrice) {
        this.id = id;
        this.category = category;
        this.count = count;
        this.unitPrice = unitPrice;
    }

    public int subtotal() {
        return count * unitPrice;
    }
}

// ========== 策略接口 ==========
interface PricingStrategy {
    double calculate(List<SaleItem> items);
}

// ========== 策略实现 ==========
class DiscountStrategy implements PricingStrategy {
    private double rate = 1.0;
    public void setRate(double rate) { this.rate = rate; }

    @Override
    public double calculate(List<SaleItem> items) {
        int sum = items.stream().mapToInt(SaleItem::subtotal).sum();
        return sum * rate;
    }
}

class FullReductionStrategy implements PricingStrategy {
    private double enoughLine = 0.0;
    private double reduction  = 0.0;
    public void setEnoughLine(double v) { this.enoughLine = v; }
    public void setReduction(double v)  { this.reduction  = v; }

    @Override
    public double calculate(List<SaleItem> items) {
        int sum = items.stream().mapToInt(SaleItem::subtotal).sum();
        return sum >= enoughLine ? sum - reduction : sum;
    }
}

// ========== 策略工厂 ==========
class StrategyFactory {
    private final Properties props = new Properties();
    private final Map<String, PricingStrategy> cache = new HashMap<>();

    public StrategyFactory(Path path) throws IOException {
        try (InputStream in = Files.newInputStream(path)) {
            props.load(in);
        }
    }

    public PricingStrategy resolveFor(SaleItem item) {
        String exactKey = "pricing.product." + item.id;
        String catKey   = "pricing.product.category:" + item.category;

        if (hasClass(exactKey)) return cache.computeIfAbsent(exactKey, this::buildFrom);
        if (hasClass(catKey))   return cache.computeIfAbsent(catKey,   this::buildFrom);
        return cache.computeIfAbsent("pricing.default", this::buildFrom);
    }

    private boolean hasClass(String prefix) {
        return props.getProperty(prefix + ".class") != null;
    }

    private PricingStrategy buildFrom(String prefix) {
        String cls = props.getProperty(prefix + ".class");
        if (cls == null) throw new IllegalStateException("Missing class for " + prefix);

        try {
            Class<?> c = Class.forName(cls);
            Object obj = c.getDeclaredConstructor().newInstance();

            // 注入参数
            String paramPrefix = prefix + ".params.";
            for (String name : props.stringPropertyNames()) {
                if (!name.startsWith(paramPrefix)) continue;
                String field = name.substring(paramPrefix.length());
                String value = props.getProperty(name);
                inject(obj, field, value);
            }

            return (PricingStrategy) obj;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void inject(Object target, String field, String value) {
        Class<?> cls = target.getClass();
        String setter = "set" + Character.toUpperCase(field.charAt(0)) + field.substring(1);

        // 先找 setter
        for (Method m : cls.getMethods()) {
            if (m.getName().equals(setter) && m.getParameterCount() == 1) {
                try {
                    m.invoke(target, convert(value, m.getParameterTypes()[0]));
                    return;
                } catch (Exception ignored) {}
            }
        }

        // 再找字段
        try {
            Field f = cls.getDeclaredField(field);
            f.setAccessible(true);
            f.set(target, convert(value, f.getType()));
        } catch (Exception e) {
            throw new RuntimeException("Cannot inject " + field, e);
        }
    }

    private Object convert(String str, Class<?> type) {
        if (type == String.class) return str;
        if (type == int.class || type == Integer.class) return Integer.parseInt(str);
        if (type == double.class || type == Double.class) return Double.parseDouble(str);
        if (type == boolean.class || type == Boolean.class) return Boolean.parseBoolean(str);
        throw new IllegalArgumentException("Unsupported param type: " + type);
    }
}

// ========== 主程序 ==========
public class Main {
    public static void main(String[] args) throws Exception {
        // 1. 构造一些示例商品
        List<SaleItem> items = new ArrayList<>();
        items.add(new SaleItem("NO1", "misc", 1, 200));
        items.add(new SaleItem("NO2", "misc", 1, 150));
        items.add(new SaleItem("P100", "electronics", 1, 800));
        // 2. 加载配置
        Path conf = Paths.get("pricing.properties");
        StrategyFactory factory = new StrategyFactory(conf);

        // 3. 计算每件商品价格
        double total = 0.0;
        for (SaleItem it : items) {
            PricingStrategy strat = factory.resolveFor(it);
            double price = strat.calculate(Collections.singletonList(it));
            System.out.printf("Item %s -> %s => %.2f%n", it.id,
                    strat.getClass().getSimpleName(), price);
            total += price;
        }

        System.out.printf("TOTAL = %.2f%n", total);
    }
}